package org.devgframwork.platform.utils.http;

import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 *   0、初始化连接池管理器(PoolingHttpClientConnectionManager)，创建CPool
 *   1、CPool 连接池中获取连接(ManagedHttpClientConnection)
 * 	 2、将连接bind到Socket
 * 	 3、标记连接为已完成状态(entry.markRouteComplete)
 * 	 4、通过http连接发送请求获取返回数据
 * @author duheng
 * @Date 2021/6/17 16:55
 */
public class PooledHttpClientAdaptor {

	private static final int DEFAULT_POOL_MAX_TOTAL = 900;
	private static final int DEFAULT_POOL_MAX_PER_ROUTE = 900;

//	private static final int DEFAULT_CONNECT_TIMEOUT = 500;
//	private static final int DEFAULT_CONNECT_REQUEST_TIMEOUT = 500;
//	private static final int DEFAULT_SOCKET_TIMEOUT = 2000;

	private static final int DEFAULT_CONNECT_TIMEOUT = 30000;
	private static final int DEFAULT_CONNECT_REQUEST_TIMEOUT = 30000;
	private static final int DEFAULT_SOCKET_TIMEOUT = 30000;

	private PoolingHttpClientConnectionManager gcm = null;

	private CloseableHttpClient httpClient = null;

	private IdleConnectionMonitorThread idleThread = null;

	// 连接池的最大连接数
	private final int maxTotal;
	// 连接池按route配置的最大连接数
	private final int maxPerRoute;

	// tcp connect的超时时间
	private final int connectTimeout;
	// 从连接池获取连接的超时时间
	private final int connectRequestTimeout;
	// tcp io的读写超时时间
	private final int socketTimeout;

	public PooledHttpClientAdaptor() {
		this(
				PooledHttpClientAdaptor.DEFAULT_POOL_MAX_TOTAL,
				PooledHttpClientAdaptor.DEFAULT_POOL_MAX_PER_ROUTE,
				PooledHttpClientAdaptor.DEFAULT_CONNECT_TIMEOUT,
				PooledHttpClientAdaptor.DEFAULT_CONNECT_REQUEST_TIMEOUT,
				PooledHttpClientAdaptor.DEFAULT_SOCKET_TIMEOUT
		);
	}

	public PooledHttpClientAdaptor(
			int maxTotal,
			int maxPerRoute,
			int connectTimeout,
			int connectRequestTimeout,
			int socketTimeout
	) {

		this.maxTotal = maxTotal;
		this.maxPerRoute = maxPerRoute;
		this.connectTimeout = connectTimeout;
		this.connectRequestTimeout = connectRequestTimeout;
		this.socketTimeout = socketTimeout;

		Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
				.register("http", PlainConnectionSocketFactory.getSocketFactory())
				.register("https", SSLConnectionSocketFactory.getSocketFactory())
				.build();

		this.gcm = new PoolingHttpClientConnectionManager(registry);
		this.gcm.setMaxTotal(this.maxTotal);
		this.gcm.setDefaultMaxPerRoute(this.maxPerRoute);

		RequestConfig requestConfig = RequestConfig.custom()
				.setConnectTimeout(this.connectTimeout)                     // 设置连接超时
				.setSocketTimeout(this.socketTimeout)                       // 设置读取超时
				.setConnectionRequestTimeout(this.connectRequestTimeout)    // 设置从连接池获取连接实例的超时
				.build();

		HttpClientBuilder httpClientBuilder = HttpClients.custom();
		httpClient = httpClientBuilder
				.setConnectionManager(this.gcm)
				.setDefaultRequestConfig(requestConfig)
				.build();

		idleThread = new IdleConnectionMonitorThread(this.gcm);
		idleThread.start();

	}

	public String doGet(String url) {
		return this.doGet(url, Collections.EMPTY_MAP, Collections.EMPTY_MAP);
	}

	public String doGet(String url, Map<String, Object> params) {
		return this.doGet(url, Collections.EMPTY_MAP, params);
	}

	public String doGet(String url,
			Map<String, String> headers,
			Map<String, Object> params
	) {

		// *) 构建GET请求头
		String apiUrl = getUrlWithParams(url, params);
		HttpGet httpGet = new HttpGet(apiUrl);

		// *) 设置header信息
		if ( headers != null && headers.size() > 0 ) {
			for (Map.Entry<String, String> entry : headers.entrySet()) {
				httpGet.addHeader(entry.getKey(), entry.getValue());
			}
		}
//		RequestConfig requestConfig = RequestConfig.custom()
//				.setConnectTimeout(this.connectTimeout)                     // 设置连接超时
//				.setSocketTimeout(this.socketTimeout)                       // 设置读取超时
//				.setConnectionRequestTimeout(this.connectRequestTimeout)    // 设置从连接池获取连接实例的超时
//				.build();
//
//		HttpClientBuilder httpClientBuilder = HttpClients.custom();
//		this.httpClient = httpClientBuilder
////				.setConnectionManager(this.gcm)
//				.setDefaultRequestConfig(requestConfig)
//				.build();
		CloseableHttpResponse response = null;
		try {
			response = httpClient.execute(httpGet);
			if (response == null || response.getStatusLine() == null) {
				return null;
			}

			int statusCode = response.getStatusLine().getStatusCode();
			if ( statusCode == HttpStatus.SC_OK ) {
				HttpEntity entityRes = response.getEntity();
				if (entityRes != null) {
					return EntityUtils.toString(entityRes, "UTF-8");
				}
			}
			return null;
		} catch (IOException e) {
		} finally {
//			try {
//				httpClient.close();
//			} catch (Exception ex) {
//				throw new RuntimeException(ex);
//			}
			if ( response != null ) {
				try {
					response.close();
				} catch (IOException e) {
				}
			}
		}
		return null;
	}

	public String doPost(String apiUrl, Map<String, Object> params) {
		return this.doPost(apiUrl, Collections.EMPTY_MAP, params);
	}

	public String doPost(String apiUrl,
			Map<String, String> headers,
			Map<String, Object> params
	) {

		HttpPost httpPost = new HttpPost(apiUrl);

		// *) 配置请求headers
		if ( headers != null && headers.size() > 0 ) {
			for (Map.Entry<String, String> entry : headers.entrySet()) {
				httpPost.addHeader(entry.getKey(), entry.getValue());
			}
		}

		// *) 配置请求参数
		if ( params != null && params.size() > 0 ) {
			HttpEntity entityReq = getUrlEncodedFormEntity(params);
			httpPost.setEntity(entityReq);
		}

		CloseableHttpResponse response = null;
		try {
			response = httpClient.execute(httpPost);
			if (response == null || response.getStatusLine() == null) {
				return null;
			}

			int statusCode = response.getStatusLine().getStatusCode();
			if ( statusCode == HttpStatus.SC_OK ) {
				HttpEntity entityRes = response.getEntity();
				if ( entityRes != null ) {
					return EntityUtils.toString(entityRes, "UTF-8");
				}
			}
			return null;
		} catch (IOException e) {
		} finally {
			if (response != null) {
				try {
					response.close();
				} catch (IOException e) {
				}
			}
		}
		return null;

	}

	private HttpEntity getUrlEncodedFormEntity(Map<String, Object> params) {
		List<NameValuePair> pairList = new ArrayList<NameValuePair>(params.size());
		for (Map.Entry<String, Object> entry : params.entrySet()) {
			NameValuePair pair = new BasicNameValuePair(entry.getKey(), entry
					.getValue().toString());
			pairList.add(pair);
		}
		return new UrlEncodedFormEntity(pairList, Charset.forName("UTF-8"));
	}

	private String getUrlWithParams(String url, Map<String, Object> params) {
		boolean first = true;
		StringBuilder sb = new StringBuilder(url);
		for (String key : params.keySet()) {
			char ch = '&';
			if (first == true) {
				ch = '?';
				first = false;
			}
			String value = params.get(key).toString();
			try {
				String sval = URLEncoder.encode(value, "UTF-8");
				sb.append(ch).append(key).append("=").append(sval);
			} catch (UnsupportedEncodingException e) {
			}
		}
		return sb.toString();
	}

	public void shutdown() {
		idleThread.shutdown();
	}

	// 监控有异常的链接
	private class IdleConnectionMonitorThread extends Thread {

		private final HttpClientConnectionManager connMgr;
		private volatile boolean exitFlag = false;

		public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr) {
			this.connMgr = connMgr;
			setDaemon(true);
		}

		@Override
		public void run() {
			while (!this.exitFlag) {
				synchronized (this) {
					try {
						this.wait(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				// 关闭失效的连接
				connMgr.closeExpiredConnections();
				// 可选的, 关闭30秒内不活动的连接
				connMgr.closeIdleConnections(30, TimeUnit.SECONDS);
			}
		}

		public void shutdown() {
			this.exitFlag = true;
			synchronized (this) {
				notify();
			}
		}

	}

}
